#pragma once
#include "AnimationCommon.h"
#include "TetModel.h"

class CParticleSource
{
public:
	CParticleSource() = default;
	~CParticleSource();

	void initialize(int vStretchNeighborCount);
	void setVoxelizationInfo(const SVoxelizationInfo& vVoxelizationInfo);
	void addCell(Eigen::Vector3i vLocation, Eigen::Vector3f vCellPos);	
	void addCell(int vSplitingCount, Eigen::Vector3i vLocation, Eigen::Vector3f vCellPos);	
	void generateParticles();
	void generateAllNeighbors(int vStretchNeighborCount);
	std::vector<int> findNearestVertex(const Eigen::Vector3d& vPosition);
	void setRelatedBoneIndex(int vIndex, int vRelatedBoneIndex);

	int getRelatedBoneIndex(int vIndex) const;
	Eigen::Vector3i getBoxExtendSize() const { return m_voxelInfo.filledVoxelSize; }
	float getSpacing() const { return m_voxelInfo.spacing; }
	Eigen::Vector3f getParticlePos(int vIndex) const { return m_particles[vIndex].centerPos; }
	int getParticleCount() const { return m_particles.size(); }	
	float getParticleRadius() const { return m_particleUnitLength / 2.0f; }
	int getSkinParticleCount() const { return m_skinParticleIndices.size(); }
	int getNumOfRegions(int vIndex) const { return m_particleNeighborList[vIndex].regionNeighbors.size(); }
	int getRegionsCount() const { return m_regions.size(); }
	CTetModel* getTetModel() const { return m_TetModel; }
	SRegion getRegion(int vIndex) const { return m_regions[vIndex]; }
	bool isSkinParticle(int vIndex) { return m_particles[vIndex].isSkin; }
	bool dumpSkinParticleIndices(std::vector<int>& voSkinList);
	bool dumpCommonNeighbors(int vParticleIndex, std::vector<int>& voNeighbors);
	bool dumpStretchNeighbors(int vParticleIndex, std::vector<int>& voNeighbors);
	bool dumpOneRingNeighbors(int vParticleIndex, std::vector<int>& voNeighbors);
	bool dumpRegionNeighbors(int vParticleIndex, std::vector<int>& voNeighbors);
	
#ifdef _UNIT_TEST
	CParticleSource(bool vIsTesting) {}
	int getfilledCellsCount() const { return m_cells.size(); }
	void initCellIter() { m_cellItr = m_cells.begin(); }
	void getNextCellLocation(Eigen::Vector3i& voLocation)
	{ 
		voLocation = m_cellItr->first;
		m_cellItr++;
	}	
	const SCellInfo* getCellInfo(Eigen::Vector3i vLocation) const { return &m_cells.at(vLocation); }
	const SParticle* getParticle(int vIndex) const { return &m_particles[vIndex]; }
	Eigen::Vector3f getParticleInitPostion(Eigen::Vector3f vCellCenterPos, float vSpacing, int vParticleIndex)
	{
		return __getParticleInitPostion(vCellCenterPos, vSpacing, vParticleIndex);
	}
#endif

private:	
	void __clear();
	void __createParticlesFromCells();
	bool __ifParticleCreated(Eigen::Vector3i vLocation, Eigen::Vector3f vParticlePos, int& voParticleIndice);
	Eigen::Vector3f __getParticleInitPostion(Eigen::Vector3f vCellCenterPos, float vSpacing, int vParticleIndex);	
	const SCellInfo* __getCellInfo(Eigen::Vector3i vLocation) const { return &m_cells.at(vLocation); }
	void __initNeighbors();
	void __generateOneRingNeighbors();
	void __generateCommonNeighbors();
	void __generateRegionNeighbors();
	void __generateStretchNeighbors(int vNeighborCount);
	void __generateRegions();
	bool __ifRegionCreated(const std::vector<int> vLeft, const std::vector<int> vRight);

	std::unordered_map<Eigen::Vector3i, SCellInfo, HashFuncVector3i, EqualKeyVector3i> m_cells;
	std::vector<SParticle> m_particles;
	std::vector<int> m_relatedBoneIndex;
	std::unordered_map<Eigen::Vector3i, SCellInfo, HashFuncVector3i, EqualKeyVector3i>::const_iterator m_cellItr;
	std::vector<SParticleNeighbor> m_particleNeighborList;
	std::vector<SRegion> m_regions;
	std::vector<int> m_skinParticleIndices;
	CTetModel* m_TetModel = nullptr;
	SVoxelizationInfo m_voxelInfo;
	float m_particleUnitLength = 0.0f;
};